﻿[assembly: System.CLSCompliant(true)]
namespace SeedObject
{
    using Interlocked = System.Threading.Interlocked;
    using Serializable = System.SerializableAttribute;
    using XmlIgnoreAttribute = System.Xml.Serialization.XmlIgnoreAttribute;
    using XmlAttribute = System.Xml.Serialization.XmlAttributeAttribute;

    /// <summary>
    /// Base class which will give rise to new objects
    /// </summary>
    [Serializable]
    public abstract class Seed : ISeed 
    {
        #region Fields

        //Stock Restorers
        internal System.Collections.Generic.IList<Action> Restorers;
        // List of attributes that have changed
        internal Attributes Attributes;
        // for synchronized access to object
        static int Synchronizer;

        #endregion
        #region Constructor
        // Default Constructor
        protected Seed()
            : base()
        {
            // Gets the name of the class
            _Kind = GetType().Name.ToString(Utility.CultureInfo).Split(Characters.Apostrophe)[0];
            // assigns a id based on class
            _Id = _Kind;

            // sets the state seed
            _State = State.Instance | State.Initialized;

            // Sets the properties to determine the object's article
            _Gender = Gender.Female;
            _Number = Number.Singular;
            // initializes the list of Restorers
            Restorers = new System.Collections.Generic.List<Action>();
            // initializes the list of attributes
            Attributes = new Attributes(1);
        }
        #endregion
        #region ISeed Members
        /// <summary>
        /// For the Hispanic language to other languages change the implementation
        /// </summary>
        public string Article
        {
            get
            {
                if (_Gender == Gender.Female)
                {
                    if (_Number == Number.Singular)
                        return Chains.La;
                    return Chains.Las;
                }
                if (_Gender == Gender.Male)
                {
                    if (_Number == Number.Singular)
                        return Chains.El;
                    return Chains.Los;

                }
                // indeterminate case
                if (_Number == Number.Singular)
                    return Chains.Lo;
                return Chains.Los;
            }
        }
        private string _Kind;
        /// <summary>
        /// Name of the object class
        /// </summary>
        public string Kind
        {
            get
            {
                return _Kind;
            }
        }
        private string _Id;
        /// <summary>
        /// object identifier
        /// </summary>
        [XmlAttribute("id")]
        public string Id
        {
            get
            {
                return _Id;
            }
            set
            {
                SaveAttribute(ID.Id, _Id,
                    delegate()
                    {
                        _Id = (string)Attributes[ID.Id];
                    }
                );
                _Id = value;
            }
        }
        /// <summary>
        /// lets ask whether the object is in a particular state
        /// </summary>
        /// <param name="state">state for which he is consulted</param>
        /// <returns>result of the operation</returns>
        public bool InA(State state)
        {
            return ((_State & state) == state);
        }
        private ISeed _Parent;
        /// <summary>
        /// sets the family relationship
        /// </summary>
        [XmlIgnoreAttribute]
        public ISeed Parent
        {
            get
            {
                return _Parent;
            }
            set
            {
                SaveAttribute(ID.Parent, _Parent, 
                    delegate() 
                    {
                        _Parent = (ISeed)Attributes[ID.Parent]; 
                    }
                );
                _Parent = value; 
            }
        }
        private ISeed _Proxy;
        /// <summary>
        /// allows for representation in another Context
        /// </summary>
        [XmlIgnoreAttribute]
        public ISeed Proxy
        {
            get
            {
                return _Proxy;
            }
            set
            {
                SaveAttribute(ID.Proxy, _Proxy, 
                    delegate() 
                    { 
                        _Proxy = (ISeed)Attributes[ID.Proxy]; 
                    }
                );
                _Proxy = value; 
            }
        }
        /// <summary>
        /// restores the object to their original values
        /// </summary>
        public virtual void Restore()
        {
            // restored from the last until the beginning
            // this ensures that the occupied state is changed to the last
            for (int i = Restorers.Count - 1; i >= 0; i--)
                Restorers[i]();
            Restorers.Clear();
            Attributes.Clear();
        }
        private State _State;
        /// <summary>
        /// state of de object
        /// </summary>
        [XmlIgnoreAttribute]
        public State State
        {
            get
            {
                return _State;
            }
            set
            {
                if ((((value & State.Busy) == State.Busy)) || (Busy(this)))
                {
                    if (Attributes.Add(ID.State, _State))
                        Restorers.Add(
                            delegate()
                            {
                                _State = (State)Attributes[ID.State];
                            }
                         );
                }
                _State = value;
            }
        }

        
        #endregion
        #region Seed Members
        private Gender _Gender;
        internal void SetGender(Gender value)
        {
            _Gender = value; 
        }
        private Number _Number;
        internal void SetNumber(Number value)
        {
             _Number = value; 
        }
        #endregion
        #region Seed Methods
        //determines whether the object is busy
        protected static bool Busy(ISeed obj)
        {
            if (obj.InA(State.Busy))
                return true;

            ISeed Parent = obj.Parent;
            while (Parent != null)
            {
                if (Parent.InA(State.Busy))
                    return true;
                Parent = Parent.Parent;
            }
            return false;
        }
        //Save the original value of the attribute for subsequent restoration
        internal void SaveAttribute(ID att, object value, Action restorer) 
        {
            // ocupado y si pudo registrar el atributo
            if ((Busy(this)) && (Attributes.Add(att, value)))
               Restorers.Add(restorer);
        }
        /// <summary>
        /// can hold an object securely in a multiuser environment
        /// </summary>  
        internal static bool Synchronize(ISeed obj)
        {
            bool result = false;
            if (0 == Interlocked.Exchange(ref Seed.Synchronizer, 1))
            {
                if (!obj.InA(State.Busy))
                {
                    obj.State |= State.Busy;
                    result = true;
                }
                Interlocked.Exchange(ref Seed.Synchronizer, 0);
            }
            return result;
        }
        #endregion

        #region IXmlSerializable Members
            public System.Xml.Schema.XmlSchema GetSchema()
            {
                return null;
            }
            public virtual void ReadXml(System.Xml.XmlReader reader)
            {
                // Retrieves the object identifier
                string id = reader.GetAttribute(Chains.Id);
                if (!string.IsNullOrEmpty(id))
                    Id = id;
                reader.ReadStartElement();
            }
            public virtual void WriteXml(System.Xml.XmlWriter writer)
            {
                if(!_Id.Equals(_Kind))
                    writer.WriteAttributeString(Chains.Id, Id);
            }

        #endregion
    }
}
